/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 *
 */

import { expect } from '@jest/globals';
import * as fse from 'fs-extra';
import { removeSync } from 'fs-extra';
import * as path from 'path';
import { resolve } from 'path';

import { AppJson } from '../../src/options/configure/app-json-options';
import { ModuleJson } from '../../src/options/configure/module-json-options';

import { initByStageEmptyApp, initByStageEmptyService, initExecuteCommand, runHvigor, } from './common/common';
import { ModuleBuildProfileModifier } from './common/modifier/module-build-profile-modifier';
import { PackageJsonModifier } from './common/modifier/package-json-modifier';
import { Probes } from './common/probes';
import ModuleOptObj = ModuleJson.ModuleOptObj;
import { getJson5Obj } from '../../src/utils/json-util';

describe('test process-libs Incremental', () => {
  test.concurrent('test stage app', async () => {
    await initByStageEmptyApp({
      cb: testProcessLibs,
      prepare: prepareProcessLibs,
      enableNative: true,
      runNpmInstall: true
    });
  });

  test.concurrent('test stage Service', async () => {
    await initByStageEmptyService({
      cb: testProcessLibs,
      prepare: prepareProcessLibs,
      enableNative: true,
      runNpmInstall: true
    });
  });
});

export async function prepareProcessLibs(projectRoot: string) {
  const entryPackageJson = new PackageJsonModifier(projectRoot, 'entry');
  entryPackageJson.updateDependency('library', 'file:../library');
  entryPackageJson.outputSync();
  removeSync(resolve(projectRoot, 'entry', 'libs'));
}

export async function testProcessLibs(projectRoot: string) {
  const commands = initExecuteCommand({
    mode: 'module',
    module: 'entry',
    product: 'default',
    tasks: 'default@ProcessLibs'
  });

  function serviceShouldBuildFailed(projectRoot: string, commands: string[]) {
    const appJson = path.resolve(projectRoot, 'AppScope/app.json5');
    const moduleJson = path.resolve(projectRoot, 'entry/src/main/module.json5');
    const appJsonOpt: AppJson.AppOptObj = getJson5Obj(appJson);
    const moduleJsonOpt: ModuleOptObj = getJson5Obj(moduleJson);
    if (appJsonOpt.app.bundleType === 'atomicService' || moduleJsonOpt.module.installationFree === true) {
      new ModuleBuildProfileModifier(projectRoot, 'entry').updateTargetRuntimeOS('default', 'OpenHarmony');
      new ModuleBuildProfileModifier(projectRoot, 'feature').updateTargetRuntimeOS('default', 'OpenHarmony');
      new ModuleBuildProfileModifier(projectRoot, 'slibrary').updateTargetRuntimeOS('default', 'OpenHarmony');

      const serviceResult = runHvigor(projectRoot, commands, { stderr: 'pipe' });
      expect(serviceResult.stderr
        .includes('Atomic service development is not supported in the OpenHarmony stage model.'))
        .toBeTruthy();
      return serviceResult;
    }
    return null;
  }

  function shouldBuildFinished(projectRoot: string, commands: string[]) {
    const result = runHvigor(projectRoot, commands, { stderr: 'pipe' });
    expect(result.exitCode).toEqual(0);
    expect(result.stdout.includes('BUILD SUCCESSFUL')).toBeTruthy();
    expect(result.stdout.includes('Finished :entry:default@ProcessLibs')).toBeTruthy();
    return result;
  }

  function shouldBuildUp2Date(projectRoot: string, commands: string[]) {
    const result = runHvigor(projectRoot, commands, { stderr: 'pipe' });
    expect(result.exitCode).toEqual(0);
    expect(result.stdout.includes('BUILD SUCCESSFUL')).toBeTruthy();
    expect(result.stdout.includes('UP-TO-DATE :entry:default@ProcessLibs')).toBeTruthy();
    return result;
  }

  serviceShouldBuildFailed(projectRoot, commands);
  new ModuleBuildProfileModifier(projectRoot, 'entry').updateTargetRuntimeOS('default', 'HarmonyOS');
  new ModuleBuildProfileModifier(projectRoot, 'feature').updateTargetRuntimeOS('default', 'HarmonyOS');
  new ModuleBuildProfileModifier(projectRoot, 'library').updateTargetRuntimeOS('default', 'HarmonyOS');
  new ModuleBuildProfileModifier(projectRoot, 'slibrary').updateTargetRuntimeOS('default', 'HarmonyOS');


  // 第一次执行任务
  shouldBuildFinished(projectRoot, commands);

  // 没有修改，第二次执行任务
  shouldBuildUp2Date(projectRoot, commands);

  // 修改c++代码，第三次执行任务
  const helloCppPath = path.resolve(projectRoot, 'entry', 'src', 'main', 'cpp', 'hello.cpp');
  fse.writeFileSync(helloCppPath, fse.readFileSync(helloCppPath).toString().replace('"entry"', '"entry1"'));
  shouldBuildFinished(projectRoot, commands);

  // 没有修改，第四次执行任务
  shouldBuildUp2Date(projectRoot, commands);

  // 修改build-profile中的napiFilterOptions，第五次执行任务
  const moduleBuildProfileModifier = new ModuleBuildProfileModifier(projectRoot, 'entry');
  moduleBuildProfileModifier.setNAPIFilterOptions({
    enableOverride: true
  }).outputSync();
  shouldBuildFinished(projectRoot, commands);

  // 修改target中的napiFilterOptions，第6次执行任务
  moduleBuildProfileModifier.setNAPIFilterOptions({
    excludes: [
      '**/entry.so'
    ]
  }, 'default').outputSync();
  shouldBuildFinished(projectRoot, commands);
  const probe = new Probes(projectRoot, resolve(projectRoot, 'entry'));
  expect(fse.readdirSync(resolve(probe.getIntermediatesProcessLibs(), 'armeabi-v7a'))).not.toContain('entry1.so');

  shouldBuildUp2Date(projectRoot, commands);

  // 向library中的libs文件夹添加.so文件，触发增量编译
  fse.ensureDirSync(resolve(projectRoot, 'library', 'libs', 'armeabi-v7a'));
  fse.appendFileSync(resolve(projectRoot, 'library', 'libs', 'armeabi-v7a', '1.so'), 'some-data');
  fse.appendFileSync(resolve(projectRoot, 'library', 'libs', 'armeabi-v7a', '1.so.56789'), 'some-data');
  shouldBuildFinished(projectRoot, commands);
  expect(fse.readdirSync(resolve(probe.getIntermediatesProcessLibs(), 'armeabi-v7a'))).toContain('1.so.56789');

  // 向entry中的libs文件夹添加.so文件，触发增量编译
  fse.ensureDirSync(resolve(projectRoot, 'entry', 'libs', 'armeabi-v7a'));
  fse.appendFileSync(resolve(projectRoot, 'entry', 'libs', 'armeabi-v7a', '2.so'), 'some-data');
  fse.appendFileSync(resolve(projectRoot, 'entry', 'libs', 'armeabi-v7a', '2.so.01234'), 'some-data');
  fse.appendFileSync(resolve(projectRoot, 'entry', 'libs', 'armeabi-v7a', '2.so.1.2'), 'some-data');
  fse.appendFileSync(resolve(projectRoot, 'entry', 'libs', 'armeabi-v7a', '2.so.1234qwer'), 'some-data');
  shouldBuildFinished(projectRoot, commands);
  expect(fse.readdirSync(resolve(probe.getIntermediatesProcessLibs(), 'armeabi-v7a'))).toContain('2.so.01234');
  expect(fse.readdirSync(resolve(probe.getIntermediatesProcessLibs(), 'armeabi-v7a'))).not.toContain('2.so.1234qwer');
  expect(fse.readdirSync(resolve(probe.getIntermediatesProcessLibs(), 'armeabi-v7a'))).not.toContain('2.so.1.2');
}
